package vm;

import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.Tab;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellDataFeatures;
import javafx.scene.control.TableView;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.control.ToolBar;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.Pane;
import javafx.util.Callback;
import memory.Memory;
import os.*;

/**
 * Interfeiso Kontroleris
 * @author Paulius Dambrauskas, Šarūnas Kūjalis
 */
public class MainWindowController implements Initializable {
    public ArrayList<Word> memory = new ArrayList<>();
    public Processor processor;
    public OS oSystem;
    public Memory mem;
    @FXML
    TreeView<File> fileTree;
    //Registrų lentelė
    @FXML
    TableView<Register> registerTable;
    @FXML
    TableColumn<Register, String> registerName;
    @FXML
    TableColumn<Register, String> registerValue;
    @FXML
    TextField input;
    @FXML
    CheckBox stepMode;
    @FXML
    Button stepButton;
    @FXML
    Label nextText;
    @FXML
    Label nextComm;
    @FXML
    public ToolBar toolBar;
    public Boolean step = true;
    //Realios Atminties matrica
    @FXML
    TableView realMatrix;
    @FXML
    TableColumn<List<Word>, String> blockNumber;
    //Virtualios Atminties matrica
    @FXML
    TableView virtualMatrix;
    @FXML
    TableColumn<List<Word>, String> vblockNumber;
    @FXML
    Tab vTab;
    @FXML
    TextField cmd;
    @FXML 
    TextArea concole;
    //Procesu lentele
    @FXML
    public TableView<os.Process> processTable;
    @FXML
    TableColumn<os.Process, String> processCol;
    @FXML
    TableColumn<os.Process, String> stateCol;
    @FXML
    TableColumn<os.Process, String> procStep;
   
    //resursu lentele
    @FXML
    public TableView<OSResource> resourceTable;
    @FXML
    TableColumn<OSResource, String> resourceCol; 
    @FXML
    TableColumn<OSResource, String> resCount; 
    //input
    @FXML
    public Button inputBtn;
    @FXML
    public Button userBtn;
    public String printBuffer = null;
    
    @FXML 
    public Pane redLight;
    @FXML 
    public Pane greenLight;
    
    final public String redOnStyle = "-fx-background-radius: 25; -fx-background-color: #E60505";
    final public String greenOnStyle = "-fx-background-radius: 25; -fx-background-color: #0ED900";
    final public String redOffStyle = "-fx-background-radius: 25; -fx-background-color: #EA9D9D;";
    final public String greenOffStyle = "-fx-background-radius: 25; -fx-background-color: #A4DDA0";
    
    //public Boolean isInterrupt = false;
    
    @Override
    public void initialize(URL url, ResourceBundle rb) {
        oSystem = new OS(this);
        mem = new Memory();
        processor = new Processor(this);
        stepMode.setSelected(false);
        stepMode.setDisable(false);
        initProcessTable();
        initResourceTable();
        initTreeView();
        initMemoryTable();
        initRegisterTable();
          
        initVirtualMemoryTable();
        userBtn.setDisable(true);
        inputBtn.setDisable(true);

    }
    public void initProcessTable(){
        ObservableList<os.Process> data = FXCollections.observableList(new ArrayList<>(OS.processes.values()));
        processCol.setCellValueFactory(new PropertyValueFactory<os.Process, String>("procName"));
        stateCol.setCellValueFactory(new PropertyValueFactory<os.Process, String>("state"));
        procStep.setCellValueFactory(new PropertyValueFactory<os.Process, String>("step"));
     
        processTable.setItems(data);
    }
    public void initResourceTable(){
        //System.out.println(OS.resources.size());
        ObservableList<OSResource> data = FXCollections.observableList(new ArrayList<>(OS.resources.values()));
        resourceCol.setCellValueFactory(new PropertyValueFactory<OSResource, String>("resName")); 
        resCount.setCellValueFactory(new PropertyValueFactory<OSResource, String>("count"));
        resourceTable.setItems(data);
        
    }
    /**
     * Funkcija sukurianti failų medį
     * @author Paulius Dambrauskas
     */
    private void initTreeView(){
        TreeItem<File> rootItem = new FileTreeItem(new File(VM.hard));
        fileTree.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
        fileTree.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() {
            @Override
            public void changed(ObservableValue observable, Object oldValue, Object newValue) {
                try{
                    TreeItem<File> treeItem =  (TreeItem<File>) newValue;
                    cmd.setText(treeItem.getValue().getName());
                } catch (Exception e){}
                //programArea.setText(new ProgramFile(treeItem.getValue()).content);
            }     
        });
        fileTree.setRoot(rootItem);
    }
    /**
     * Funkcija sugeneruojanti registrų lentelę
     * @author Paulius Dambrauskas
     */
    public void initRegisterTable(){
           
                registerTable.setEditable(true);
                Callback<TableColumn<Register, String>, TableCell<Register, String>> cellFactory =
                      new Callback<TableColumn<Register, String>, TableCell<Register, String>>() {
                          @Override
                          public TableCell call(TableColumn p) {
                              return new EditingRegister();
                          }
                      };
                ObservableList<Register> data = processor.getRegisters();

                registerName.setCellValueFactory(new PropertyValueFactory<Register, String>("registerName"));
                registerValue.setCellValueFactory(new PropertyValueFactory<Register, String>("registerValue"));
                registerValue.setCellFactory(cellFactory);
                registerValue.setOnEditCommit(
                      new EventHandler<TableColumn.CellEditEvent<Register, String>>() {
                          @Override 
                          public void handle(TableColumn.CellEditEvent<Register, String> t) {

                              (t.getTableView().getItems().get(
                                      t.getTablePosition().getRow())).setRegisterValue(Integer.parseInt(t.getNewValue(), 16)+"");


                          }
                      });
                registerTable.setItems(data);
            
    }
    
    /**
     * Funkcija sugeneruojanti Relaios atminties lentelę
     * @author Paulius Dambrauskas
     */
    private void initMemoryTable(){
        ObservableList<Word> data = Memory.getMemory();
        //Matrica:
        TableColumn col;
        blockNumber.setCellValueFactory(new Callback<CellDataFeatures<List<Word>, String>, ObservableValue<String>>() {
                @Override
                public ObservableValue<String> call(CellDataFeatures<List<Word>, String> p) {
                    String col = p.getTableColumn().getText();
                    return new SimpleStringProperty(p.getValue().get(1).address.get().substring(1, 3));
                }
            });
        //Stulpeliai
        for(int i = 0; i < 16; i++){
            col = new TableColumn<>(Integer.toHexString(i).toUpperCase());
            col.setPrefWidth(40.0);
            col.setCellValueFactory(new Callback<CellDataFeatures<List<Word>, String>, ObservableValue<String>>() {
                @Override
                public ObservableValue<String> call(CellDataFeatures<List<Word>, String> p) {
                    String col = p.getTableColumn().getText();
                    return p.getValue().get(Integer.parseInt(col, 16)).wordValue;
                }
            });
            col.setId(i+"");
            realMatrix.getColumns().add(col);
        }
        //Eilutės
        int k =0;
        for (int j = 0; j < VM.realMemorySize; j+=16){
            List<Word> part = data.subList(j, j+16);
            realMatrix.getItems().add(k, part);
            k++;
        }
    }
    /**
     * Sugeneruoja virtualios atminties matricą
     * @author Paulius Dambrauskas
     */
    public void initVirtualMemoryTable(){
         if(processor != null){
            //Matrica:
            TableColumn col;
            //Pirmas stulpelis
            vblockNumber.setCellValueFactory(new Callback<CellDataFeatures<List<Word>, String>, ObservableValue<String>>() {
                    @Override
                    public ObservableValue<String> call(CellDataFeatures<List<Word>, String> p) {
                        String col = p.getTableColumn().getText();
                        return new SimpleStringProperty(p.getValue().get(1).virtualAddress.get().substring(2, 3));
                    }
                });
            //žodžių stulpeliai
            for(int i = 0; i < 16; i++){
                col = new TableColumn<String, String>(Integer.toHexString(i).toUpperCase());
                col.setPrefWidth(40.0);
                col.setEditable(true);
                col.setCellValueFactory(new Callback<CellDataFeatures<List<Word>, String>, ObservableValue<String>>() {
                    @Override
                    public ObservableValue<String> call(CellDataFeatures<List<Word>, String> p) {
                        String col = p.getTableColumn().getText();
                        return p.getValue().get(Integer.parseInt(col, 16)).wordValue;
                    }
                });
                col.setCellFactory(new Callback<TableColumn<Word, String>, TableCell<Word, String>>() {
                    @Override
                    public TableCell call(TableColumn p) {
                        return new EditingWord();
                    }
                });
               col.setOnEditCommit(
                  new EventHandler<TableColumn.CellEditEvent<Word, String>>() {
                      @Override 
                      public void handle(TableColumn.CellEditEvent<Word, String> t) {

                          Memory.setMemory(
                                  processor.getRealAddress(t.getTablePosition().getRow()*16+t.getTablePosition().getColumn()-1), 
                                  t.getNewValue()
                                  );

                      }
                  });

                virtualMatrix.getColumns().add(col);
            }
            //Eilutės
            int k =0;
            for (int j = 0; j < VM.numOfBlocks*VM.numOfWords; j+=16){

                virtualMatrix.getItems().add(k, null);

                k++;
            }
        
        }  
    }
    /**
     * Užpildo virtualios atminties matricą
     * @author Paulius Dambrauskas
     */
    public void initVirtualMemory(){
        
        if(processor != null){
            vTab.setDisable(false);
            ObservableList<Word> data = processor.getVirtualMemory();
            int k =0;
            for (int j = 0; j < VM.numOfBlocks*VM.numOfWords; j+=16){
                List<Word> part = data.subList(j, j+16);
                virtualMatrix.getItems().set(k, part);
                k++;
            }
        }
       
    }
    /**
     * Mygtuko „Vykdyti“ veiksmas
     * @param e 
     */
    @FXML
    private void runApp(ActionEvent e){
        
            
        if(stepMode.isSelected()){
            stepButton.setVisible(true);
            step(null);
            nextText.setVisible(true);
            nextComm.setVisible(true);
        } else {
            //processor.run();
            step(null);
            
            //nextText.setVisible(false);
            //nextComm.setVisible(false);
        }   
    }
    
    private void loopProgram() {
        //Boolean step = true;
        while (step) {
            step(null);
        }
    }
    /**
     * Mygtuko „Žengti“ veiksmas
     * @param e 
     */
    @FXML
    private void step(ActionEvent e){
            
            if (printBuffer != null) {
                appendConsole(printBuffer);
                printBuffer = null;
                //OS.freeRes(new ResourceElement("Spausdinimo pabaiga", true));
            }
            os.Process proc = OS.processes.get(OS.currentProcess);
            if(proc.step == 0)
                proc.setStep(1);
            if(VM.mode == 1){
                proc.runProcess();
                nextComm.setVisible(false);
                nextText.setVisible(false);
                //System.out.println(OS.processes.get(OS.currentProcess).getClass().getName()+" trololo");
            } else {
                proc.step();
                nextComm.setVisible(true);
                nextText.setVisible(true);
                int NextPc = Integer.parseInt(processor.getPC().registerValue.get());
                nextComm.setText(Memory.getMemory().get(processor.getRealAddress(NextPc)).wordValue.get());
            }
           
            
       
   }
    
   @FXML
   private void inputResource(ActionEvent e){
       OS.freeRes(new ResourceElement("Įvestis", (Object)cmd.getText()));
      if (!stepMode.isSelected()) {
           loopProgram();
       }
   }
    /**
     * Pertraukimo lango uždarymas
     * @author Paulius Dambrauskas
     * @param e 
     */
    @FXML
    private void userInput(ActionEvent e){
        //OS.freeRes(new ResourceElement("Vartotojo įvestis", input.getText()));
        OS.processes.get(OS.currentProcess).extra = input.getText();
        this.userBtn.setDisable(true);
        this.toolBar.setDisable(false);
        if (!stepMode.isSelected()) {
            step = true;
            loopProgram();
        }
    }
    public void appendConsole(String s){
       this.concole.appendText(s);
    }
    
}
